package top.mindse.office2pdf.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.jacob.com.ComThread;
import com.jacob.com.LibraryLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.mindse.office2pdf.aspect.LogRecord;
import top.mindse.office2pdf.handler.ExcelHandler;
import top.mindse.office2pdf.handler.PowerPointHandler;
import top.mindse.office2pdf.handler.WordHandler;
import top.mindse.office2pdf.util.PDFUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;


/**
 * 转换接口
 */

@RestController
@RequestMapping("/office")
public class Office2PDFController {
    private static final Logger logger = LoggerFactory.getLogger(Office2PDFController.class);

    private static final String TMPDIR = System.getProperty("java.io.tmpdir");

    @PostConstruct
    public void init() {
        URL resource = Thread.currentThread().getContextClassLoader().getResource("");
        if (Objects.isNull(resource)) {
            return;
        }
        String path = resource.getPath();
        String dll = path + "dll/" + LibraryLoader.getPreferredDLLName() + ".dll";
        if (new File(dll).exists()) {
            System.setProperty(LibraryLoader.JACOB_DLL_PATH, dll);
            ComThread.InitMTA();
        }
    }


    @GetMapping("/convert")
    @LogRecord(desc = "修改了订单的配送员：从“{queryOldUser{#request.deliveryOrderNo()}}”, 修改到“{deveryUser{#request.userId}}”,fileName=#request.deliveryOrderNo")
    public void convertOffice(@RequestParam("fileName") String fileName, @RequestParam("fileUrl") String fileUrl, @RequestParam(value = "watermark", required = false) String watermark) {
        if (StrUtil.isBlank(fileName)) {
            throw new RuntimeException("文件名称不能为空");
        }
        if (StrUtil.isBlank(fileUrl)) {
            throw new RuntimeException("文件路径不能为空");
        }
        String format = fileName.substring(fileName.lastIndexOf(".") + 1);
        //下载源文件
        File tempSourceFile = download(fileName, fileUrl);
        // 转PDF
        File tempTargetFile = convert2PDF(format, tempSourceFile);
        ComThread.Release();
        // 添加水印
        tempTargetFile = addWaterMark(watermark, tempTargetFile);
        //返回文件
        callBackPDF(tempTargetFile);
    }


    /**
     * 文件下载
     *
     * @param format
     * @param fileUrl
     * @return
     */
    private File download(String format, String fileUrl) {
        //定义请求头的接收类型
        RequestCallback requestCallback = request -> request.getHeaders()
                .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
        //对响应进行流式处理而不是将其全部加载到内存中
        RestTemplate restTemplate = new RestTemplate();
        String tempSourceFilePath = TMPDIR + IdUtil.fastSimpleUUID() + "." + format;
        File tempSourceFile = restTemplate.execute(fileUrl, HttpMethod.GET, requestCallback, clientHttpResponse -> {
            Files.copy(clientHttpResponse.getBody(), Paths.get(tempSourceFilePath));
            return new File(tempSourceFilePath);
        });
        if (Objects.isNull(tempSourceFile) || tempSourceFile.length() == 0) {
            throw new RuntimeException("文件下载失败");
        }
        return tempSourceFile;
    }

    /**
     * 转换为 PDF
     *
     * @param tempSourceFile
     * @return
     */
    private File convert2PDF(String format, File tempSourceFile) {
        if (Objects.isNull(tempSourceFile) || tempSourceFile.length() == 0) {
            throw new RuntimeException("文件不存在");
        }
        String tempTargetFilePath = TMPDIR + IdUtil.fastSimpleUUID() + ".pdf";
        File tempTargetFile = new File(tempTargetFilePath);
        if (tempSourceFile.exists()) {
            if (format.startsWith("doc")) {
                WordHandler.toPdf(tempSourceFile.getAbsolutePath(), tempTargetFile.getAbsolutePath());
            } else if (format.startsWith("xls")) {
                ExcelHandler.toPdf(tempSourceFile.getAbsolutePath(), tempTargetFile.getAbsolutePath());
            } else if (format.startsWith("ppt")) {
                PowerPointHandler.toPdf(tempSourceFile.getAbsolutePath(), tempTargetFile.getAbsolutePath());
            }
        } else {
            logger.error("文件不存在:{}", tempSourceFile.getAbsolutePath());
        }
        if (tempSourceFile.length() == 0) {
            throw new RuntimeException("文件转换错误：文件为空");
        }
        return tempTargetFile;
    }

    /**
     * 添加水印
     *
     * @param watermark
     * @param tempTargetFile
     * @return
     */
    private File addWaterMark(String watermark, File tempTargetFile) {
        if (StrUtil.isBlank(watermark) || Objects.isNull(tempTargetFile) || tempTargetFile.length() == 0) {
            return tempTargetFile;
        }
        String tempWatermarkFilePath = TMPDIR + IdUtil.fastSimpleUUID() + ".pdf";
        File tempWatermarkFile = new File(tempWatermarkFilePath);
        //pdf 添加水印
        try (InputStream in = new FileInputStream(tempTargetFile); OutputStream out = new FileOutputStream(tempWatermarkFile)) {
            PDFUtils.addWatermark(in, out, watermark);
        } catch (Exception e) {
            logger.error("PDF添加水印异常", e);
        }
        return tempWatermarkFile;
    }


    /**
     * 返回 PDF
     *
     * @param tempTargetFile
     */
    private void callBackPDF(File tempTargetFile) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(attributes)) {
            return;
        }
        HttpServletResponse response = attributes.getResponse();
        if (Objects.isNull(response)) {
            return;
        }
        try (FileInputStream input = new FileInputStream(tempTargetFile)) {
            byte[] data = new byte[input.available()];
            input.read(data);
            response.getOutputStream().write(data);
        } catch (Exception e) {
            logger.error("文件下载异常", e);
        }
    }


    /**
     * 返回 PDF
     *
     * @param tempTargetFile
     */
    private void callBackDownloadPDF(File tempTargetFile) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(attributes)) {
            return;
        }
        HttpServletResponse response = attributes.getResponse();
        if (Objects.isNull(response)) {
            return;
        }
        // 清空response
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("成功.pdf", Charset.defaultCharset()));
        // 告知浏览器文件的大小
        response.addHeader("Content-Length", "" + tempTargetFile.length());
        try (FileInputStream input = new FileInputStream(tempTargetFile)) {
            byte[] data = new byte[input.available()];
            input.read(data);
            response.getOutputStream().write(data);
        } catch (Exception e) {
            logger.error("文件下载异常", e);
        }
    }

}
